package cn.seaboot.admin.security.context;

import cn.seaboot.admin.user.bean.User;
import cn.seaboot.admin.user.bean.UserDept;
import cn.seaboot.admin.user.context.UserContext;
import cn.seaboot.admin.user.service.UserDeptService;
import cn.seaboot.admin.user.service.UserService;
import cn.seaboot.commons.core.Asserts;
import cn.seaboot.commons.core.Converter;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
 * Security实现 -- 用户上下文，对于不同的鉴权框架，需要有统一的标准，获取登录的用户信息。
 *
 * @author ChenSS on 2018年4月18日
 */
@Service
public class SecurityUserContext implements UserContext {
    private Logger logger = LoggerFactory.getLogger(SecurityUserContext.class);

    @Resource
    private UserDeptService userDeptService;

    @Resource
    private UserService userService;

    //  User----------------------------

    /**
     * 获取当前登录的用户
     *
     * @return User
     * @throws AccessDeniedException user already logout or authentication-token timeout
     */
    @Override
    public @NotNull User getUser() {
        // 注意 SecurityContext 不能做成成员变量
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

        if (authentication == null) {
            throw new AccessDeniedException("user already logout or authentication-token timeout！");
        }
        Object username = authentication.getPrincipal();
        return userService.queryByAccount(Converter.toString(username));
    }

    /**
     * 获取当前登录的用户ID
     *
     * @return 用户ID
     */
    @Override
    public @NotNull String getUserId() {
        return getUser().getId();
    }

    /**
     * 获取用户名
     *
     * @return 用户名
     */
    @Override
    public @NotNull String getUsername() {
        return getUser().getUsername();
    }

    /**
     * 获取用户所在公司ID
     *
     * @return 公司ID
     */
    @Override
    public @NotNull String getOrgId() {
        return getUser().getOrgId();
    }

    //  Group----------------------------

    /**
     * 查询用户分组
     *
     * @return 用户分组
     */
    @Override
    public @NotNull UserDept getUserDept() {
        UserDept userDept = userDeptService.queryById(getUserDeptId());
        Asserts.notNull(userDept, "user dept is undefined!");
        return userDept;
    }

//     /**
//      * 查询用户角色
//      *
//      * @return 角色
//      */
//     @Override
//     public Role getUserRole() {
//         return userGroupService.queryRoleByGroupId(getUserDeptId());
//     }
//
//     /**
//      * 获取用户角色ID
//      *
//      * @return 角色ID
//      */
//     @Override
//     public String getUserRoleId() {
//         return getUserDept().getRoleId();
//     }

    /**
     * 数据的层级查询，基本遵循1个原则，只查询下级的数据，如果要查看同级的数据，可以用PID进行查询。
     *
     * @return 部门ID
     */
    @Override
    public @NotNull String getUserDeptPid() {
        return this.getUserDept().getPid();
    }

    /**
     * 获取用户所在的部门ID，用于维护数据的上下层级关系
     *
     * @return 部门ID
     */
    @Override
    public @NotNull String getUserDeptId() {
        return this.getUser().getDeptId();
    }

//     /**
//      * （业务场景：仅某角色可以执行相关操作）
//      * 获取角色编码，除ID之外的唯一标识，用于制作权限系统，与Security的hasRole()语法对应。
//      * 注意：编码会被写到代码中，随意编写不生效，权限定好后，不允许后期修改。
//      *
//      * @return 角色编码
//      */
//     @Override
//     public String getUserRoleCode() {
//         return getUserRole().getRoleCode();
//     }

    /**
     * （业务场景：仅某分组可以执行相关操作）
     * 获取分组编码，除ID之外的唯一标识，用于制作权限系统，与Security的hasRole()语法对应。
     * 注意：编码会被写到代码中，随意编写不生效，权限定好后，不允许后期修改。
     *
     * @return 分组编码
     */
    @Override
    public @NotNull String getUserDeptCode() {
        return this.getUserDept().getDeptCode();
    }

    /**
     * 与MySQL查询相关，与全国行政区划类似，上级行政区划为ax，下级必定以ax开头，例如ax11，查询的时候，使用INSTR()查询
     *
     * @return 用户所在分组
     */
    @Override
    public @NotNull String getUserPath() {
        return this.getUserDept().getPath();
    }

    /**
     * 目标分组是否是登录用户的直属上级
     *
     * @param groupId 目标部门ID
     * @return boolean
     */
    @Override
    public boolean isMySuperior(String groupId) {
        UserDept userDept = userDeptService.queryById(groupId);
        Asserts.notNull(userDept, "user dept is undefined!");
        return userDeptService.isSuperior(userDept, getUserDept());
    }

    /**
     * 目标分组是否是登录用户的直属上级
     *
     * @param target 目标分组
     * @return boolean
     */
    @Override
    public boolean isMySuperior(UserDept target) {
        return userDeptService.isSuperior(target, this.getUserDept());
    }

    /**
     * 目标分组是否是登录用户的下属
     *
     * @param groupId 目标部门ID
     * @return boolean
     */
    @Override
    public boolean isMyUnderling(String groupId) {
        return userDeptService.isUnderling(userDeptService.queryById(groupId), this.getUserDept());
    }

    /**
     * 目标分组是否是登录用户的下属
     *
     * @param target 目标分组
     * @return boolean
     */
    @Override
    public boolean isMyUnderling(UserDept target) {
        return userDeptService.isUnderling(target, this.getUserDept());
    }
}
